package com.example.productserver.service.impl;

import com.example.productserver.common.LocalCache;
import com.example.productserver.common.constant.RedisKeyPrefixConst;
import com.example.productserver.entity.CyGoods;
import com.example.productserver.exception.CustomException;
import com.example.productserver.model.dto.ProductIdDTO;
import com.example.productserver.model.vo.ProductInfoVO;
import com.example.productserver.result.ResultData;
import com.example.productserver.result.ResultUtil;
import com.example.productserver.service.CyGoodsService;
import com.example.productserver.service.ProductService;
import com.example.productserver.util.RedisOpsUtil;
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.redisson.api.RReadWriteLock;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

@Service
public class ProductServiceImpl implements ProductService {

    @Autowired
    private CyGoodsService cyGoodsService;


    @Autowired
    private LocalCache cache;

    @Autowired
    private RedisOpsUtil redisOpsUtil;

    @Autowired
    private Redisson redisson;

    private final static ReentrantLock  lock=new ReentrantLock();

    public static final String LOCK_PRODUCT_HOT_CACHE_PREFIX = "lock:product:hot_cache:";

    public static final String LOCK_PRODUCT_UPDATE_PREFIX = "lock:product:update:";

    @Override
    public ResultData<ProductInfoVO> selectProductInfoList(ProductIdDTO productIdDTO) {
        Integer pid = productIdDTO.getId();
        //首先,从本地缓存中获取商品详情信息
        ProductInfoVO productInfoVO=new ProductInfoVO();
        CyGoods cyGoods=null;

        cyGoods=cache.getCache(RedisKeyPrefixConst.PRODUCT_DETAIL_CACHE+pid);
        if(cyGoods!=null){
            BeanUtils.copyProperties(cyGoods,productInfoVO);
            return ResultUtil.getResultData(productInfoVO);
        }
        //否则,从redis缓冲中获取商品详情信息
        cyGoods=redisOpsUtil.get(RedisKeyPrefixConst.PRODUCT_DETAIL_CACHE+pid,ProductInfoVO.class);
        if(cyGoods!=null){

            //刷新本地内存的数据，保持最新的热点数据
            cache.setLocalCache(RedisKeyPrefixConst.PRODUCT_DETAIL_CACHE+pid,cyGoods);
            BeanUtils.copyProperties(cyGoods,productInfoVO);
            return ResultUtil.getResultData(productInfoVO);
        }
        //控制高并发
        //方案一:考虑单体应用，使用JVM锁
        //否则，从数据库中获取商品详情信息
/*
        try {
            //DCL双重检测
            cyGoods=cache.getCache(RedisKeyPrefixConst.PRODUCT_DETAIL_CACHE+pid);
            if(cyGoods!=null){
                BeanUtils.copyProperties(cyGoods,productInfoVO);
                return ResultUtil.getResultData(productInfoVO);
            }
            lock.lock();      //加锁
            //查询数据库
            cyGoods = cyGoodsService.getCyGoodsInfo(pid).getData();
            System.out.println("查询数据库");
            //属性赋值
            BeanUtils.copyProperties(cyGoods,productInfoVO);
            //设置本地缓存
            cache.setLocalCache(RedisKeyPrefixConst.PRODUCT_DETAIL_CACHE+pid,cyGoods);
            //设置redis缓存---设置过期时间，主要考虑瞬时并发和数据一致性
            redisOpsUtil.set(RedisKeyPrefixConst.PRODUCT_DETAIL_CACHE+pid,cyGoods,3600, TimeUnit.SECONDS);

        }catch (Exception e){
            throw new CustomException("商品未上架");
        } finally {
            lock.unlock();
        }
        */


        //方案一:考虑分布式系统,使用分布式锁
        RLock hotCacheLock = redisson.getLock(LOCK_PRODUCT_HOT_CACHE_PREFIX + pid);
        hotCacheLock.lock();
        try {
            //DCL双重检测
            cyGoods=cache.getCache(RedisKeyPrefixConst.PRODUCT_DETAIL_CACHE+pid);
            if(cyGoods!=null){
                BeanUtils.copyProperties(cyGoods,productInfoVO);
                return ResultUtil.getResultData(productInfoVO);
            }

            RReadWriteLock readWriteLock = redisson.getReadWriteLock(LOCK_PRODUCT_UPDATE_PREFIX + pid);
            RLock rLock = readWriteLock.readLock();
            rLock.lock();
            try {
                //查询数据库
                cyGoods = cyGoodsService.getCyGoodsInfo(pid).getData();
                System.out.println("查询数据库");
                //属性赋值
                BeanUtils.copyProperties(cyGoods,productInfoVO);
                //设置本地缓存
                cache.setLocalCache(RedisKeyPrefixConst.PRODUCT_DETAIL_CACHE+pid,cyGoods);
                //设置redis缓存---设置过期时间，主要考虑瞬时并发和数据一致性
                redisOpsUtil.set(RedisKeyPrefixConst.PRODUCT_DETAIL_CACHE+pid,cyGoods,3600, TimeUnit.SECONDS);

            } catch (CustomException e){
                throw new CustomException("商品未上架");
            }finally {
                rLock.unlock();
            }
        }catch (CustomException e){
            throw new CustomException("商品未上架");
        }finally {
            hotCacheLock.unlock();
        }
        return ResultUtil.getResultData(productInfoVO);
    }
}
